import serial
import time
import threading
import logging

class LoraController:
    """LoRa模块控制器，封装AT指令操作"""

    def __init__(self, port, baudrate=115200, timeout=1):
        """
        初始化LoRa控制器
        
        参数:
            port: 串口名称
            baudrate: 波特率
            timeout: 超时时间(秒)
        """
        self.port = port
        self.baudrate = baudrate
        self.timeout = timeout
        self.serial = None
        self.is_connected = False
        self.receive_thread = None
        self.receiving = False
        self.in_transparent_mode = False
        self.callback = None
        
        # 数据缓冲区相关
        self.data_buffer = ""
        self.last_data_time = 0
        self.buffer_timeout = 0.2  # 200毫秒的缓冲超时时间
        
        # 配置日志
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s'
        )
        self.logger = logging.getLogger("LoraController")
    
    def connect(self):
        """连接到LoRa模块"""
        try:
            self.serial = serial.Serial(
                port=self.port,
                baudrate=self.baudrate,
                timeout=self.timeout
            )
            self.is_connected = True
            self.logger.info(f"已连接到 {self.port}")
            return True
        except Exception as e:
            self.logger.error(f"连接失败: {str(e)}")
            return False
    
    def disconnect(self):
        """断开与LoRa模块的连接"""
        if self.serial and self.is_connected:
            # 如果在透传模式，先退出
            if self.in_transparent_mode:
                self.exit_transparent_mode()
            
            # 发送缓冲区中的最后数据
            if self.data_buffer and self.callback:
                self.callback(self.data_buffer, "text")
                self.data_buffer = ""
                
            # 停止接收线程
            self.stop_receiving()
            
            # 关闭串口
            self.serial.close()
            self.is_connected = False
            self.logger.info("已断开连接")
            return True
        return False
    
    def send_command(self, command, wait_response=True, timeout=2):
        """
        发送AT指令到LoRa模块
        
        参数:
            command: AT指令
            wait_response: 是否等待响应
            timeout: 等待响应超时时间(秒)
            
        返回:
            响应内容或None
        """
        if not self.is_connected or not self.serial:
            self.logger.error("未连接到设备")
            return None
        
        # 确保指令以\r\n结尾
        if not command.endswith("\r\n"):
            command += "\r\n"
        
        try:
            self.logger.info(f"发送指令: {command.strip()}")
            self.serial.write(command.encode())
            
            if wait_response:
                start_time = time.time()
                response = ""
                
                while (time.time() - start_time) < timeout:
                    if self.serial.in_waiting:
                        chunk = self.serial.read(self.serial.in_waiting).decode(errors='replace')
                        response += chunk
                        
                        # 检查是否有完整响应
                        if len(response.strip()) > 0:
                            # 有响应内容，并且超过200毫秒没有新数据，认为响应结束
                            time.sleep(0.2)
                            if self.serial.in_waiting == 0:
                                break
                    
                    time.sleep(0.1)
                
                self.logger.debug(f"收到响应: '{response}'")
                return response
            return None
        except Exception as e:
            self.logger.error(f"发送指令失败: {str(e)}")
            return None
    
    def set_callback(self, callback):
        """设置接收数据的回调函数"""
        self.callback = callback
    
    def start_receiving(self, hex_format=True):
        """开始接收数据线程"""
        if self.receiving:
            return
        
        # 清空数据缓冲区
        self.data_buffer = ""
        self.last_data_time = time.time()
        
        self.receiving = True
        self.receive_thread = threading.Thread(target=self._receive_loop, args=(hex_format,))
        self.receive_thread.daemon = True
        self.receive_thread.start()
        self.logger.info("开始接收数据")
    
    def stop_receiving(self):
        """停止接收数据线程"""
        # 发送缓冲区中的最后数据
        if self.data_buffer and self.callback:
            self.callback(self.data_buffer, "text")
            self.data_buffer = ""
            
        self.receiving = False
        if self.receive_thread and self.receive_thread.is_alive():
            self.receive_thread.join(1)
        self.logger.info("停止接收数据")
    
    def _receive_loop(self, hex_format):
        """接收数据循环"""
        while self.receiving and self.is_connected and self.serial:
            try:
                if self.serial.in_waiting:
                    data = self.serial.read(self.serial.in_waiting)
                    
                    if self.callback:
                        if hex_format:
                            # 十六进制模式下直接回调，不进行缓冲
                            hex_str = ' '.join([f"{b:02X}" for b in data])
                            self.callback(hex_str, "hex")
                        else:
                            # 文本模式下进行缓冲处理
                            text = data.decode(errors='replace')
                            current_time = time.time()
                            
                            # 添加到缓冲区
                            self.data_buffer += text
                            self.last_data_time = current_time
                            
                            # 检查是否为完整消息
                            should_flush = False
                            
                            # 模式1: LoRa模块接收完成消息（包含rssi和snr信息）
                            if ("rssi =" in self.data_buffer and "snr =" in self.data_buffer and 
                                "from:" in self.data_buffer):
                                should_flush = True
                            
                            # 模式2: 如果缓冲区包含完整的OnRxDone消息但已经停留超过100ms
                            elif "OnRxDone" in self.data_buffer and (current_time - self.last_data_time) > 0.1:
                                should_flush = True
                            
                            # 模式3: 如果缓冲区包含任何OK、ERROR回复且已停留50ms以上
                            elif (("OK" in self.data_buffer or "ERROR" in self.data_buffer or "Get CMD" in self.data_buffer) 
                                  and (current_time - self.last_data_time) > 0.05):
                                should_flush = True
                            
                            # 如果需要刷新缓冲区，发送缓冲的消息
                            if should_flush and self.callback:
                                self.callback(self.data_buffer, "text")
                                self.data_buffer = ""
                else:
                    # 检查是否需要发送缓冲区数据
                    current_time = time.time()
                    if (self.data_buffer and 
                        (current_time - self.last_data_time) > self.buffer_timeout):
                        # 超过缓冲超时时间，发送缓冲区数据
                        if self.callback:
                            self.callback(self.data_buffer, "text")
                        # 清空缓冲区
                        self.data_buffer = ""
            except Exception as e:
                self.logger.error(f"接收数据错误: {str(e)}")
                time.sleep(1)
            
            time.sleep(0.01)
    
    def exit_transparent_mode(self):
        """退出透传模式"""
        if not self.in_transparent_mode:
            return True
        
        try:
            # 清空数据缓冲区
            self.data_buffer = ""
            
            # 发送+++退出透传模式
            time.sleep(1)  # 先等待1秒
            self.logger.info("发送退出透传模式命令: +++")
            self.serial.write("+++".encode())
            time.sleep(1)  # 等待退出透传模式
            
            # 再发送回车换行确认
            self.serial.write("\r\n".encode())
            time.sleep(0.5)
            
            # 读取退出透传模式的响应
            response = ""
            start_time = time.time()
            while (time.time() - start_time) < 3:
                if self.serial.in_waiting:
                    chunk = self.serial.read(self.serial.in_waiting).decode(errors='replace')
                    response += chunk
                    self.logger.info(f"退出透传响应: '{response}'")
                    
                    # 检查是否包含退出透传的特定响应
                    if "Quit transparent" in response:
                        time.sleep(0.5)  # 等待一小段时间确认没有更多数据
                        if self.serial.in_waiting == 0:
                            self.in_transparent_mode = False
                            self.logger.info("已退出透传模式")
                            return True
                time.sleep(0.1)
            
            self.logger.warning("退出透传模式无明确响应，尝试继续退出操作")
            
            # 即使没有收到明确响应，也假设已退出
            self.in_transparent_mode = False
            
            # 尝试发送AT命令测试是否真的退出了透传模式
            test_response = self.send_command("AT", wait_response=True, timeout=1)
            if test_response:
                self.logger.info("成功退出透传模式并收到AT响应")
            else:
                self.logger.warning("可能未完全退出透传模式")
                
            return True
        except Exception as e:
            self.logger.error(f"退出透传模式错误: {str(e)}")
            return False
    
    def send_transparent_data(self, data):
        """
        在透传模式下发送数据
        
        参数:
            data: 要发送的数据字符串
        """
        if not self.is_connected or not self.serial:
            self.logger.error("未连接到设备")
            return False
        
        if not self.in_transparent_mode:
            self.logger.error("未处于透传模式，无法发送透传数据")
            return False
        
        try:
            # 确保以\r\n结尾
            if not data.endswith("\r\n"):
                data += "\r\n"
            
            self.logger.info(f"发送透传数据: {data.strip()}")
            self.serial.write(data.encode())
            self.serial.flush()  # 确保数据被发送出去
            
            # 等待一小段时间，看是否有响应
            time.sleep(0.2)
            if self.serial.in_waiting:
                response = self.serial.read(self.serial.in_waiting).decode(errors='replace')
                self.logger.info(f"透传数据响应: '{response}'")
            
            return True
        except Exception as e:
            self.logger.error(f"发送透传数据失败: {str(e)}")
            return False
    
    # ================ AT指令封装 ================
    
    def set_tx_config(self, freq, data_rate, bandwidth, code_rate, power, iq_converted=0):
        """
        配置发射参数并进入透传模式 (AT+CTX)
        
        参数:
            freq: 频率(Hz)
            data_rate: 扩频因子(0-7)，对应SF12-SF5
            bandwidth: 带宽(0-9)
            code_rate: 编码率(1-4)
            power: 发射功率(0-22)
            iq_converted: IQ转换(0-1)
            
        返回:
            是否成功进入透传模式
        """
        cmd = f"AT+CTX={freq},{data_rate},{bandwidth},{code_rate},{power},{iq_converted}"
        response = self.send_command(cmd)
        
        # 打印完整响应以便调试
        self.logger.info(f"透传模式响应: '{response}'")
        
        # 检查是否有"Get CMD"响应，表示命令已被接收
        if response and "Get CMD" in response:
            self.in_transparent_mode = True
            self.logger.info("已进入透传模式")
            return True
        
        self.logger.warning(f"进入透传模式失败，未收到预期响应")
        return False
    
    def set_rx_config_hex(self, freq, data_rate, bandwidth, code_rate, iq_converted=0):
        """
        配置接收参数并启动接收(HEX格式) (AT+CRX)
        
        参数:
            freq: 频率(Hz)
            data_rate: 扩频因子(0-7)，对应SF12-SF5
            bandwidth: 带宽(0-9)
            code_rate: 编码率(1-4)
            iq_converted: IQ转换(0-1)
            
        返回:
            是否成功启动接收
        """
        cmd = f"AT+CRX={freq},{data_rate},{bandwidth},{code_rate},{iq_converted}"
        response = self.send_command(cmd)
        
        if response and ("Get CMD" in response or "OK" in response):
            self.start_receiving(hex_format=True)
            return True
        
        self.logger.warning(f"启动HEX接收模式失败，响应: {response}")
        return False
    
    def set_rx_config_string(self, freq, data_rate, bandwidth, code_rate, iq_converted=0):
        """
        配置接收参数并启动接收(字符串格式) (AT+CRXS)
        
        参数:
            freq: 频率(Hz)
            data_rate: 扩频因子(0-7)，对应SF12-SF5
            bandwidth: 带宽(0-9)
            code_rate: 编码率(1-4)
            iq_converted: IQ转换(0-1)
            
        返回:
            是否成功启动接收
        """
        cmd = f"AT+CRXS={freq},{data_rate},{bandwidth},{code_rate},{iq_converted}"
        response = self.send_command(cmd)
        
        if response and ("Get CMD" in response or "OK" in response):
            self.start_receiving(hex_format=False)
            return True
        
        self.logger.warning(f"启动字符串接收模式失败，响应: {response}")
        return False
    
    def set_local_address(self, addr):
        """
        设置本地地址 (AT+CADDRSET)
        
        参数:
            addr: 本地地址(16位无符号整数)
            
        返回:
            是否设置成功
        """
        cmd = f"AT+CADDRSET={addr}"
        response = self.send_command(cmd)
        
        if response and "Get CMD" in response:
            self.logger.info(f"本地地址设置为 {addr}")
            return True
        
        self.logger.warning(f"设置本地地址失败，响应: {response}")
        return False
    
    def set_target_address(self, addr):
        """
        设置目标地址 (AT+CTXADDRSET)
        
        参数:
            addr: 目标地址(16位无符号整数)
            
        返回:
            是否设置成功
        """
        cmd = f"AT+CTXADDRSET={addr}"
        response = self.send_command(cmd)
        
        if response and "Get CMD" in response:
            self.logger.info(f"目标地址设置为 {addr}")
            return True
        
        self.logger.warning(f"设置目标地址失败，响应: {response}")
        return False
    
    def set_sleep_mode(self, mode):
        """
        设置睡眠模式 (AT+CSLEEP)
        
        参数:
            mode: 睡眠模式(0-1)，0为热启动，1为冷启动
            
        返回:
            是否设置成功
        """
        cmd = f"AT+CSLEEP={mode}"
        response = self.send_command(cmd)
        
        if response and "Get CMD" in response:
            self.logger.info(f"已进入睡眠模式，唤醒模式: {'冷启动' if mode == 1 else '热启动'}")
            return True
        
        self.logger.warning(f"设置睡眠模式失败，响应: {response}")
        return False
    
    # ================ 辅助方法 ================
    
    def get_available_serial_ports(self):
        """获取系统可用的串口列表"""
        import serial.tools.list_ports
        ports = serial.tools.list_ports.comports()
        return [port.device for port in ports] 